import { LoadingOutlined } from '@ant-design/icons';
import { ProSkeleton, RequestData } from '@ant-design/pro-components';
import { DescriptionsPageSkeletonProps } from '@ant-design/pro-skeleton/es/components/Descriptions';
import { ListPageSkeletonProps } from '@ant-design/pro-skeleton/es/components/List';
import { Empty, Skeleton, Spin, SpinProps } from 'antd';
import type { EmptyProps } from 'antd/es/empty';
import { SkeletonProps } from 'antd/es/skeleton';
import lodash from 'lodash';
import React, { ReactNode } from 'react';
import ErrorView, { ErrorViewProps } from '../ErrorView';

type ProSkeletonProps = ListPageSkeletonProps &
  DescriptionsPageSkeletonProps & {
    type?: 'list' | 'result' | 'descriptions';
    active?: boolean;
  };

export type SfwLoadingContainerProps = (
  | SpinProps
  | SkeletonProps
  | ProSkeletonProps
) & {
  isContainer?: boolean;
  loading?: boolean;
  loadingType?: 'spin' | 'icon' | 'skeleton' | 'proSkeleton';
  loadingNode?: ReactNode;
  emptyNode?: ReactNode;
  emptyProps?: EmptyProps;
  children?: ReactNode;
  queryResult: RequestData<any>;
  errorView?: ErrorViewProps;
};

type LoadingNodeType = {
  desc: (loading: boolean, props: any) => ReactNode;
  spin: (loading: boolean, props: any) => ReactNode;
  icon: (loading: boolean, props: any) => ReactNode;
  skeleton: (loading: boolean, props: any) => ReactNode;
  proSkeleton: (loading: boolean, props: any) => ReactNode;
};

const loadingNodes: LoadingNodeType = {
  desc: () => (
    <>
      <div
        style={{
          width: '100%',
          paddingTop: 50,
          paddingBottom: 50,
          textAlign: 'center',
        }}
      >
        <Spin />
      </div>
    </>
  ),
  spin: (loading, props) => <Spin {...props} spinning={loading} />,
  icon: (loading, props) =>
    !loading ? null : <LoadingOutlined {...lodash.omit(props, 'children')} />,
  skeleton: (loading, props) => (
    <Skeleton {...props} loading={loading} active />
  ),
  proSkeleton: (loading, props) => (
    <ProSkeleton {...props} loading={loading} active />
  ),
};

type LoadingOfChildrenNodeType = {
  spin: (loading: boolean, props: any, children: any) => ReactNode;
};

const loadingOfChildrenNodes: LoadingOfChildrenNodeType = {
  spin: (loading, props, children) => (
    <Spin
      {...props}
      spinning={loading}
    >
      {children}
    </Spin>
  ),
};

const SfwLoadingContainer: React.FC<SfwLoadingContainerProps> = (props) => {
  const {
    isContainer = false,
    loadingType = 'desc',
    loadingNode,
    emptyNode: emptyNodeProps,
    emptyProps,
    children,
    queryResult,
    errorView,
    loading: loadingProps,
    ...restProps
  } = props;

  const { loading: loadingQueryResult, error, data } = queryResult;
  const loading = !lodash.isUndefined(loadingProps)
    ? loadingProps
    : lodash.isUndefined(loadingQueryResult) || loadingQueryResult;
  const queryDataEmpty = !data || lodash.isEmpty(data) || lodash.isNull(data);

  if (isContainer) {
    if (error) {
      return <ErrorView error={error} {...errorView} />;
    }
    // 支持容器加载的方式
    // @ts-ignore
    return <>{loadingOfChildrenNodes['spin'](loading, restProps, children)}</>;
  }

  const contentDomRender = () => {
    if (error) {
      return <ErrorView error={error} {...errorView} />;
    }
    if (loading) {
      return loadingNode || loadingNodes[loadingType](loading, restProps);
    }
    if (!loading && queryDataEmpty) {
      return emptyNodeProps || <Empty {...emptyProps} />;
    }

    return children;
  };

  return <>{contentDomRender()}</>;
};

export default SfwLoadingContainer;
